/**
 * <br>项目名: fastdfs-util
 * <br>文件名: FastdfsUtil.java
 * <br>Copyright 2015 恒昌互联网运营中心
 */
package com.credithc.fastdfs.util;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.ProtoCommon;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.StructGroupStat;
import org.csource.fastdfs.StructStorageStat;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <br>
 * 类 名: FastdfsUtil <br>
 * 描 述: 描述类完成的主要功能 <br>
 * 作 者: zj <br>
 * 创 建： 2015年4月28日 <br>
 * 版 本：v1.0.0 <br>
 * <br>
 * 历 史: (版本) 作者 时间 注释
 */
public class FastdfsUtil {
	private static final transient Logger LOGGER = LoggerFactory.getLogger(FastdfsUtil.class);
	
	private static String FILE_SYSTEM_URL="http://file.credithc.com/";
	
	private static final String NEW_LINE = "\n";

	private static class SingletonHolder {
		static final FastdfsUtil instance = new FastdfsUtil();
	}

	public static FastdfsUtil getInstance() {
		return SingletonHolder.instance;
	}

	private static TrackerClient trackerClient = null;
	
	/*
	static {
		try {
			ClientGlobal.init(PathUtil.getClassPath() + "fdfs_client.conf");
			if (trackerClient == null) {
				trackerClient = new TrackerClient();
			}
		} catch (Exception e) {
			throw new RuntimeException("读取fdfs_client.conf配置文件异常：" + e.getMessage());
		}
	}
	*/
	
	public TrackerClient getTrackerClient(){
		try {
			if (trackerClient == null) {
				ClientGlobal.init(PathUtil.getClassPath() + "fdfs_client.conf");
				trackerClient = new TrackerClient();
			}
			return trackerClient;
		} catch (Exception e) {
			throw new RuntimeException("读取fdfs_client.conf配置文件异常：" + e.getMessage());
		}
	}
	
	/**
	 * 
	 * <br>描 述：获取TrackerServer连接
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @return
	 */
	public TrackerServer getTrackerServer() {
		TrackerServer trackerServer = getTrackerClient().getConnection();
		return trackerServer;
	}

	/**
	 * 
	 * <br>描 述：关闭Tracker连接
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param tracker
	 */
	public void closeTrackerServer(TrackerServer tracker) {
		try {
			if (tracker != null) {
				tracker.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * <br>描 述：获取存储Storage连接客户端
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @return
	 */
	public StorageClient1 getStorageClient() {
		StorageClient1 client = new StorageClient1(getTrackerServer(), null);
		return client;
	}

	
	
	/**
	 * 
	 * <br>描 述：上传文件
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param filePath 本地文件路径
	 * @param metaMap 文件元属性，key-value
	 * @return
	 * @throws IOException
	 */
	public String uploadFile(String filePath, Map<String, String> metaMap) throws IOException {
		File file=new File(filePath);
		return uploadFile(file, file.getName(), metaMap);
	}
	/**
	 * 
	 * <br>描 述：上传文件
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param file 文件
	 * @param metaMap 文件元属性，key-value
	 * @return
	 * @throws IOException
	 */
	public String uploadFile(File file, Map<String, String> metaMap) throws IOException {
		return uploadFile(file, file.getName(), metaMap);
	}
	/**
	 * 
	 * <br>描 述：上传文件
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param file 文件
	 * @param metaMap 文件元属性，key-value
	 * @return
	 * @throws IOException
	 */
	public String uploadFile(File file,String originalName, Map<String, String> metaMap) throws IOException {
		NameValuePair[] meta_list = null;
		if (metaMap == null) {
			metaMap = new HashMap<String, String>();
		}
		metaMap.put("originalName", originalName);
		meta_list = new NameValuePair[metaMap.size()];
		int i = 0;
		for (String key : metaMap.keySet()) {
			String value = metaMap.get(key);
			meta_list[i++] = new NameValuePair(key, value);
		}

		TrackerServer trackerServer = getTrackerServer();
		String fileId = null;
		try {
			StorageClient1 client = new StorageClient1(trackerServer, null);
			String fileExtName=getExtensionName(originalName);
			fileId = client.upload_file1(file.getAbsolutePath(), fileExtName, meta_list);
		} catch (Exception ex) {
			LOGGER.error(ex.getMessage(),ex);
		} finally {
			closeTrackerServer(trackerServer);
		}
		return fileId;
	}
	
	/**
	 * 
	 * <br>
	 * 描 述：上传文件 <br>
	 * 作 者：zj <br>
	 * 历 史: (版本) 作者 时间 注释
	 * 
	 * @param fileBytes 文件字节
	 * @param originalName 原始文件名
	 * @param metaMap 文件元属性，key-value
	 * @return
	 */
	public String uploadFile(byte[] fileBytes, String originalName, Map<String, String> metaMap) {
		NameValuePair[] meta_list = null;
		if (metaMap == null) {
			metaMap = new HashMap<String, String>();
		}
		metaMap.put("originalName", originalName);
		meta_list = new NameValuePair[metaMap.size()];
		int i = 0;
		for (String key : metaMap.keySet()) {
			String value = metaMap.get(key);
			meta_list[i++] = new NameValuePair(key, value);
		}

		TrackerServer trackerServer = getTrackerServer();
		String fileId = null;
		try {
			StorageClient1 client = new StorageClient1(trackerServer, null);
			String fileExtName=getExtensionName(originalName);
			fileId = client.upload_file1(fileBytes, fileExtName, meta_list);
			
		} catch (Exception ex) {
			LOGGER.error(ex.getMessage(),ex);
		} finally {
			closeTrackerServer(trackerServer);
		}
		return fileId;
	}
	
	/**
	 * 
	 * <br>描 述：上传从文件（比如：缩略图）
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param masterFileId 主文件ID（组名+文件名）
	 * @param suffixName 从文件的后缀名
	 * @param file 从文件
	 * @param metaMap 元数据信息
	 * @return 从文件的FileId(组名+文件名)
	 * @throws IOException
	 */
	public String uploadSlaveFile(String masterFileId,String suffixName,File file, Map<String, String> metaMap) throws IOException {
		NameValuePair[] meta_list = null;
		if (metaMap == null) {
			metaMap = new HashMap<String, String>();
		}
		meta_list = new NameValuePair[metaMap.size()];
		int i = 0;
		for (String key : metaMap.keySet()) {
			String value = metaMap.get(key);
			meta_list[i++] = new NameValuePair(key, value);
		}

		TrackerServer trackerServer = getTrackerServer();
		String slaveName = null;
		try {
			StorageClient1 client = new StorageClient1(trackerServer, null);
			slaveName=client.upload_file1(masterFileId, "_".concat(suffixName), file.getAbsolutePath(),getExtensionName(masterFileId),meta_list);
		} catch (Exception ex) {
			LOGGER.error(ex.getMessage(),ex);
		} finally {
			closeTrackerServer(trackerServer);
		}
		return slaveName;
	}

	
	/**
	 * 
	 * <br>描 述：上传从文件（比如：缩略图） <br>
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param masterFileId 主文件名
	 * @param suffixName 从文件名后缀,如：200x300
	 * @param fileBytes
	 * @param metaMap
	 * @return
	 */
	public String uploadSlaveFile(String masterFileId,String suffixName,byte[] fileBytes, Map<String, String> metaMap) {
		NameValuePair[] meta_list = null;
		if (metaMap == null) {
			metaMap = new HashMap<String, String>();
		}
		meta_list = new NameValuePair[metaMap.size()];
		int i = 0;
		for (String key : metaMap.keySet()) {
			String value = metaMap.get(key);
			meta_list[i++] = new NameValuePair(key, value);
		}

		TrackerServer trackerServer = getTrackerServer();
		String slaveName = null;
		try {
			StorageClient1 client = new StorageClient1(trackerServer, null);
			slaveName=client.upload_file1(masterFileId, "_".concat(suffixName), fileBytes,getExtensionName(masterFileId),meta_list);
		} catch (Exception ex) {
			LOGGER.error(ex.getMessage(),ex);
		} finally {
			closeTrackerServer(trackerServer);
		}
		return slaveName;
	}
	/**
	 * 
	 * <br>描 述：根据fileId(含group名)获取文件的下载链接URL地址
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param fileId 文件ID（组名+文件名）
	 * @return
	 */
	public String getFileUrl(String fileId){
		String url="";
		
		String[] parts = new String[2];
		StorageClient1.split_file_id(fileId, parts);
		String groupName=parts[0];
		String fileName=parts[1];
		TrackerServer trackerServer=null;
		try {
			trackerServer=getTrackerServer();
	        StorageServer server=getTrackerClient().getStoreStorage(trackerServer, groupName);
	        url="http://"+server.getInetSocketAddress().getHostString()+"/"+fileId;
	        server.close();
        } catch (IOException e) {
	        e.printStackTrace();
        }finally{
        	closeTrackerServer(trackerServer);
        }
		
		return url;
	}
	

	/**
	 * 
	 * <br>描 述：下载文件
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param fileId 文件ID（组名+文件名）
	 * @return
	 */
	public byte[] downloadFile(String fileId){
		TrackerServer trackerServer = getTrackerServer();
		byte[] bytes=null;
		try {
			StorageClient1 client = new StorageClient1(trackerServer, null);
			bytes=client.download_file1(fileId);
		} catch (Exception ex) {
			LOGGER.error(ex.getMessage(),ex);
		} finally {
			closeTrackerServer(trackerServer);
		}
		return bytes;
	}
	/**
	 * 
	 * <br>描 述：下载文件到本地硬盘
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param fileId 文件ID（组名+文件名）
	 * @param localFilePath 本地路径
	 * @return 0为成功，非0为失败
	 */
	public int downloadFile(String fileId,String localFilePath) {
		TrackerServer trackerServer = getTrackerServer();
		int ret=-1;
		try {
			StorageClient1 client = new StorageClient1(trackerServer, null);
			ret=client.download_file1(fileId,localFilePath);
		}catch(Exception ex){
			LOGGER.error(ex.getMessage(),ex);
		}  finally {
			closeTrackerServer(trackerServer);
		}
		return ret;
	}
	/**
	 * 
	 * <br>描 述：获取文件元数据信息
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param fileId
	 * @return
	 */
	public Map<String, String> getMetaMap(String fileId){
		TrackerServer trackerServer = getTrackerServer();
		int ret=-1;
		Map<String, String> metaMap=new HashMap<String, String>();
		try {
			
			StorageClient1 client = new StorageClient1(trackerServer, null);
			NameValuePair[] pairs=client.get_metadata1(fileId);
			
			
			for(NameValuePair pair:pairs){
				metaMap.put(pair.getName(), pair.getValue());
			}
			
		}catch(Exception ex){
			LOGGER.error(ex.getMessage(),ex);
		}  finally {
			closeTrackerServer(trackerServer);
		}
		return  metaMap;
	}
	/**
	 * 
	 * <br>描 述：获取文件元数据信息
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param fileId
	 * @param key
	 * @return
	 */
	public String getMetaMap(String fileId,String key){
		Map<String, String> metaMap=this.getMetaMap(fileId);
		return metaMap.get(key);
	}
	
	
	
	/**
	 * 
	 * <br>描 述：删除文件
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param fileId 文件ID（组名+文件名）
	 * @return 0为成功，非0为失败
	 */
	public int deleteFile(String fileId){
		TrackerServer trackerServer = getTrackerServer();
		int ret=-1;
		try {
			StorageClient1 client = new StorageClient1(trackerServer, null);
			ret=client.delete_file1(fileId);
		}catch(Exception ex){
			LOGGER.error(ex.getMessage(),ex);
		}  finally {
			closeTrackerServer(trackerServer);
		}
		return ret;
	}
	protected void appendNewLine(StringBuilder sb, String line) {
		sb.append(line);
		sb.append(NEW_LINE);
	}
	/**
	 * 
	 * <br>描 述：获取扩展名
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param filename
	 * @return
	 */
	public String getExtensionName(String filename) {   
        if ((filename != null) && (filename.length() > 0)) {   
            int dot = filename.lastIndexOf('.');   
            if ((dot >-1) && (dot < (filename.length() - 1))) {   
                return filename.substring(dot + 1);   
            }   
        }   
        return filename;   
    }   
	
	/**
	 * 
	 * <br>描 述：获取文件名（不含扩展名）
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @param filename
	 * @return
	 */
    public String getFileNameNoExt(String filename) {   
        if ((filename != null) && (filename.length() > 0)) {   
            int dot = filename.lastIndexOf('.');   
            if ((dot >-1) && (dot < (filename.length()))) {   
                return filename.substring(0, dot);   
            }   
        }   
        return filename;   
    }   

	/**
	 * 
	 * <br>描 述：监控各存储节点状态信息
	 * <br>作 者：zj
	 * <br>历 史: (版本) 作者 时间 注释
	 * @return
	 */
	public String getMonitorInfo() {
		StringBuilder sb = new StringBuilder();
		TrackerServer trackerServer = getTrackerServer();
		try {
			int count;
			appendNewLine(sb, "The MoitorInfo is:");
			StructGroupStat[] groupStats = trackerClient.listGroups(trackerServer);
			if (groupStats == null) {
				sb.append("ERROR! list groups error, error no: " + trackerClient.getErrorCode());
				LOGGER.error(sb.toString());
				return sb.toString();
			}
			appendNewLine(sb, "group count: " + groupStats.length);
			count = 0;
			for (StructGroupStat groupStat : groupStats) {
				count++;
				appendNewLine(sb, "Group " + count + ":");
				appendNewLine(sb, "group name = " + groupStat.getGroupName());
				appendNewLine(sb, "disk total space = " + groupStat.getTotalMB() + "MB");
				appendNewLine(sb, "disk free space = " + groupStat.getFreeMB() + " MB");
				appendNewLine(sb, "trunk free space = " + groupStat.getTrunkFreeMB() + " MB");
				appendNewLine(sb, "storage server count = " + groupStat.getStorageCount());
				appendNewLine(sb, "active server count = " + groupStat.getActiveCount());
				appendNewLine(sb, "storage server port = " + groupStat.getStoragePort());
				appendNewLine(sb, "storage HTTP port = " + groupStat.getStorageHttpPort());
				appendNewLine(sb, "store path count = " + groupStat.getStorePathCount());
				appendNewLine(sb, "subdir count per path = " + groupStat.getSubdirCountPerPath());
				appendNewLine(sb, "current write server index = " + groupStat.getCurrentWriteServer());
				appendNewLine(sb, "current trunk file id = " + groupStat.getCurrentTrunkFileId());

				StructStorageStat[] storageStats = trackerClient.listStorages(trackerServer, groupStat.getGroupName());
				if (storageStats == null) {
					appendNewLine(sb, "ERROR! list storage error, error no: " + trackerClient.getErrorCode());
					LOGGER.error(sb.toString());
					break;
				}

				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				int stroageCount = 0;
				for (StructStorageStat storageStat : storageStats) {
					stroageCount++;
					appendNewLine(sb, "\tStorage " + stroageCount + ":");
					appendNewLine(sb, "\t\tstorage id = " + storageStat.getId());
					appendNewLine(sb, "\t\tip_addr = " + storageStat.getIpAddr() + "  " + ProtoCommon.getStorageStatusCaption(storageStat.getStatus()));
					appendNewLine(sb, "\t\thttp domain = " + storageStat.getDomainName());
					appendNewLine(sb, "\t\tversion = " + storageStat.getVersion());
					appendNewLine(sb, "\t\tjoin time = " + df.format(storageStat.getJoinTime()));
					appendNewLine(sb, "\t\tup time = " + (storageStat.getUpTime().getTime() == 0 ? "" : df.format(storageStat.getUpTime())));
					appendNewLine(sb, "\t\ttotal storage = " + storageStat.getTotalMB() + "MB");
					appendNewLine(sb, "\t\tfree storage = " + storageStat.getFreeMB() + "MB");
					appendNewLine(sb, "\t\tupload priority = " + storageStat.getUploadPriority());
					appendNewLine(sb, "\t\tstore_path_count = " + storageStat.getStorePathCount());
					appendNewLine(sb, "\t\tsubdir_count_per_path = " + storageStat.getSubdirCountPerPath());
					appendNewLine(sb, "\t\tstorage_port = " + storageStat.getStoragePort());
					appendNewLine(sb, "\t\tstorage_http_port = " + storageStat.getStorageHttpPort());
					appendNewLine(sb, "\t\tcurrent_write_path = " + storageStat.getCurrentWritePath());
					appendNewLine(sb, "\t\tsource ip_addr = " + storageStat.getSrcIpAddr());
					appendNewLine(sb, "\t\tif_trunk_server = " + storageStat.isTrunkServer());
					appendNewLine(sb, "\t\tconntion.alloc_count  = " + storageStat.getConnectionAllocCount());
					appendNewLine(sb, "\t\tconntion.current_count  = " + storageStat.getConnectionCurrentCount());
					appendNewLine(sb, "\t\tconntion.max_count  = " + storageStat.getConnectionMaxCount());
					appendNewLine(sb, "\t\ttotal_upload_count = " + storageStat.getTotalUploadCount());
					appendNewLine(sb, "\t\tsuccess_upload_count = " + storageStat.getSuccessUploadCount());
					appendNewLine(sb, "\t\ttotal_append_count = " + storageStat.getTotalAppendCount());
					appendNewLine(sb, "\t\tsuccess_append_count = " + storageStat.getSuccessAppendCount());
					appendNewLine(sb, "\t\ttotal_modify_count = " + storageStat.getTotalModifyCount());
					appendNewLine(sb, "\t\tsuccess_modify_count = " + storageStat.getSuccessModifyCount());
					appendNewLine(sb, "\t\ttotal_truncate_count = " + storageStat.getTotalTruncateCount());
					appendNewLine(sb, "\t\tsuccess_truncate_count = " + storageStat.getSuccessTruncateCount());
					appendNewLine(sb, "\t\ttotal_set_meta_count = " + storageStat.getTotalSetMetaCount());
					appendNewLine(sb, "\t\tsuccess_set_meta_count = " + storageStat.getSuccessSetMetaCount());
					appendNewLine(sb, "\t\ttotal_delete_count = " + storageStat.getTotalDeleteCount());
					appendNewLine(sb, "\t\tsuccess_delete_count = " + storageStat.getSuccessDeleteCount());
					appendNewLine(sb, "\t\ttotal_download_count = " + storageStat.getTotalDownloadCount());
					appendNewLine(sb, "\t\tsuccess_download_count = " + storageStat.getSuccessDownloadCount());
					appendNewLine(sb, "\t\ttotal_get_meta_count = " + storageStat.getTotalGetMetaCount());
					appendNewLine(sb, "\t\tsuccess_get_meta_count = " + storageStat.getSuccessGetMetaCount());
					appendNewLine(sb, "\t\ttotal_create_link_count = " + storageStat.getTotalCreateLinkCount());
					appendNewLine(sb, "\t\tsuccess_create_link_count = " + storageStat.getSuccessCreateLinkCount());
					appendNewLine(sb, "\t\ttotal_delete_link_count = " + storageStat.getTotalDeleteLinkCount());
					appendNewLine(sb, "\t\tsuccess_delete_link_count = " + storageStat.getSuccessDeleteLinkCount());
					appendNewLine(sb, "\t\ttotal_upload_bytes = " + storageStat.getTotalUploadBytes());
					appendNewLine(sb, "\t\tsuccess_upload_bytes = " + storageStat.getSuccessUploadBytes());
					appendNewLine(sb, "\t\ttotal_append_bytes = " + storageStat.getTotalAppendBytes());
					appendNewLine(sb, "\t\tsuccess_append_bytes = " + storageStat.getSuccessAppendBytes());
					appendNewLine(sb, "\t\ttotal_modify_bytes = " + storageStat.getTotalModifyBytes());
					appendNewLine(sb, "\t\tsuccess_modify_bytes = " + storageStat.getSuccessModifyBytes());
					appendNewLine(sb, "\t\ttotal_download_bytes = " + storageStat.getTotalDownloadloadBytes());
					appendNewLine(sb, "\t\tsuccess_download_bytes = " + storageStat.getSuccessDownloadloadBytes());
					appendNewLine(sb, "\t\ttotal_sync_in_bytes = " + storageStat.getTotalSyncInBytes());
					appendNewLine(sb, "\t\tsuccess_sync_in_bytes = " + storageStat.getSuccessSyncInBytes());
					appendNewLine(sb, "\t\ttotal_sync_out_bytes = " + storageStat.getTotalSyncOutBytes());
					appendNewLine(sb, "\t\tsuccess_sync_out_bytes = " + storageStat.getSuccessSyncOutBytes());
					appendNewLine(sb, "\t\ttotal_file_open_count = " + storageStat.getTotalFileOpenCount());
					appendNewLine(sb, "\t\tsuccess_file_open_count = " + storageStat.getSuccessFileOpenCount());
					appendNewLine(sb, "\t\ttotal_file_read_count = " + storageStat.getTotalFileReadCount());
					appendNewLine(sb, "\t\tsuccess_file_read_count = " + storageStat.getSuccessFileReadCount());
					appendNewLine(sb, "\t\ttotal_file_write_count = " + storageStat.getTotalFileWriteCount());
					appendNewLine(sb, "\t\tsuccess_file_write_count = " + storageStat.getSuccessFileWriteCount());
					appendNewLine(sb, "\t\tlast_heart_beat_time = " + df.format(storageStat.getLastHeartBeatTime()));
					appendNewLine(sb, "\t\tlast_source_update = " + df.format(storageStat.getLastSourceUpdate()));
					appendNewLine(sb, "\t\tlast_sync_update = " + df.format(storageStat.getLastSyncUpdate()));
					appendNewLine(sb,
					        "\t\tlast_synced_timestamp = " + df.format(storageStat.getLastSyncedTimestamp()) + getSyncedDelayString(storageStats, storageStat));
				}
			}

		} catch (Exception ex) {

		} finally {
			closeTrackerServer(trackerServer);
		}

		return sb.toString();
	}

	protected static String getSyncedDelayString(StructStorageStat[] storageStats, StructStorageStat currentStorageStat) {
		long maxLastSourceUpdate = 0;
		for (StructStorageStat storageStat : storageStats) {
			if (storageStat != currentStorageStat && storageStat.getLastSourceUpdate().getTime() > maxLastSourceUpdate) {
				maxLastSourceUpdate = storageStat.getLastSourceUpdate().getTime();
			}
		}

		if (maxLastSourceUpdate == 0) {
			return "";
		}

		if (currentStorageStat.getLastSyncedTimestamp().getTime() == 0) {
			return " (never synced)";
		}

		int delaySeconds = (int) ((maxLastSourceUpdate - currentStorageStat.getLastSyncedTimestamp().getTime()) / 1000);
		int day = delaySeconds / (24 * 3600);
		int remainSeconds = delaySeconds % (24 * 3600);
		int hour = remainSeconds / 3600;
		remainSeconds %= 3600;
		int minute = remainSeconds / 60;
		int second = remainSeconds % 60;
		String delayTimeStr;
		if (day != 0) {
			delayTimeStr = String.format("%1$d days %2$02dh:%3$02dm:%4$02ds", day, hour, minute, second);
		} else if (hour != 0) {
			delayTimeStr = String.format("%1$02dh:%2$02dm:%3$02ds", hour, minute, second);
		} else if (minute != 0) {
			delayTimeStr = String.format("%1$02dm:%2$02ds", minute, second);
		} else {
			delayTimeStr = String.format("%1$ds", second);
		}

		return " (" + delayTimeStr + " delay)";
	}
}
